import hashlib

import PIL.Image

from os import mkdir, listdir, unlink
from os.path import (isfile, join, lexists, split, isabs, abspath, basename,
                     splitext, getsize, dirname)

class File(object):
    """File class"""
    
    class Text(object):
        """Text file class
        
        Args:
            file: an instance of File
        """
        
        def __init__(self, file):
            self.file = file
            
        def append(self, value):
            """Append string to end of file.
            
            Args:
                value: A string to append at the end of file.
            """
            
            f = open(self.file.path, 'a')
            f.write(value)
            f.close()
            
        def write(self, value):
            """Write string to file.
            
            Args:
                value: A string to write to file.
            """
            
            f = open(self.file.path, 'w')
            f.write(value)
            f.close()
        
        def writelines(self, values):
            """Write sequence of string to file.
            
            Args:
                values: A sequence of string to write to file.
            """
            
            f = open(self.file.path, 'w')
            f.writelines(values)
            f.close()
        
        def read(self, size=None):
            f = open(self.file.path, 'r')
            if size: data = f.read(size)
            else: data = f.read()
            f.close()
            return data
        
    IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tif']
    
    def __init__(self, path):
        """Initialize file class.
        
        Args:
            path: A path to file.
        """
        self._path = path
        self._image = None
        self._reportHook = None
        self._md5 = None
        self._isImage = None
    
    def __str__(self):
        return self._path
        
    @property
    def path(self):
        """A read only string of file path"""
        
        return self._path
    
    @property
    def absPath(self):
        if isabs(self._path): return self._path
        else: return abspath(self._path)
    
    @property
    def exists(self):
        """A boolean indicating path existence"""
        
        return lexists(self.path)
    
    @property
    def basename(self):
        """A string representation of name.extension"""
        
        return basename(self.path)
    
    @property
    def name(self):
        """A string of name without extension"""
        
        return splitext(self.basename)[0]
    
    @property
    def extension(self):
        """A string of extension"""
        
        return splitext(self.basename)[1][1:]
    
    @property
    def isImage(self):
        """A boolean indicating file is image or not.
        
        Checking done by trying to check the extension then open the file by PIL
        Image.open so it may be slow to check, but it's a sure kill method.
        """
        
        if self._isImage is None:
            self._isImage = False
            if self.exists and self.extension.lower() in self.IMAGE_EXTENSIONS:
                try:
                    self._image = PIL.Image.open(self.path)
                    self._isImage = True
                except: pass
            
        return self._isImage
        
    @property
    def image(self):
        """A PIL image or None"""
        
        if not self.exists: return None
        
        if self._image is None:
            try: self._image = PIL.Image.open(self.path)
            except: pass
        
        return self._image
    
    @property
    def reportHook(self):
        """A function that will called periodically when hashing.
        
        The function need to have 2 arguments of total and progress
        """
        
        return self._reportHook
    
    @reportHook.setter
    def reportHook(self, value):
        self._reportHook = value
    
    @property
    def md5(self):
        """A string of file's md5. None if file missing."""
        
        if self._md5: return self._md5
        
        if not self.exists: return None
        
        counter = 0
        block = 128
        size = getsize(self.path)
        md5 = hashlib.md5()
        
        if self.reportHook: self.reportHook(size, 1)
        
        with open(self.path, 'rb') as f:
            for chunk in iter(lambda: f.read(block), ''):
                md5.update(chunk)
                counter += 1
                if self.reportHook: self.reportHook(size, counter * block)
        self._md5 = md5.hexdigest()
        
        return self._md5
    
    def validate(self):
        """Check if path is truly file"""
        
        return isfile(self.path)
    
    def unlink(self):
        """Delete file"""
        
        unlink(self.path)
        
    _directory = None
    
    @property
    def directory(self):
        """An instance of Directory class where the file reside"""
        
        if self._directory: return self._directory
        
        from _directory import Directory
        
        self._directory = Directory(dirname(self.absPath))
        return self._directory
    
    _text = None
    
    @property
    def text(self):
        """An instance of Text sub class.
        
        Used for text file operation such as append and write.
        """
        
        if self._text: return self._text
        
        self._text = self.Text(self)
        
        return self._text